/** 
* @file		uart.c
* @date		November 22, 2012
* @brief	UART configuration and support files
* @see		USART/USART_TwoBoards/DataExchangeInterrupt/main.c 
*/

#include "uart.h"


/* Private define ------------------------------------------------------------*/
#define		MAX_BUF_LENGTH			5

/* Private typedef -----------------------------------------------------------*/
typedef struct {
	uint32_t	HeadIdx;
	uint32_t	TailIdx;
	uint8_t		Elements[MAX_BUF_LENGTH];
} Buffer;

/* Private macro -------------------------------------------------------------*/
// Check buf is full or not
#define __BUF_IS_FULL(head, tail) 				(tail==(head+1))
// Check buf is empty
#define __BUF_IS_EMPTY(headidx, tailidx) 	(headidx==tailidx)
// Reset buf 
#define __BUF_RESET(bufidx)								(bufidx=0)
#define __BUF_INCR(bufidx)								(bufidx=(bufidx+1)%MAX_BUF_LENGTH)
#define __BUF_REMAIN(headidx, tailidx)		((headidx>=tailidx)?(MAX_BUF_LENGTH-headidx+tailidx-1):(tailidx-headidx-1))

/* Private variables ---------------------------------------------------------*/
Buffer UART_TxBuffer;
volatile uint32_t UART_TxIrqState = RESET;


/**
* @brief  Configure USART1 for UART at a specific baud rate.
* @param  baud_rate		Baud rate of UART
* @retval None
*/
void UART_Config(uint32_t baud_rate) {
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
	
	/* Turn on RCC components: USART1 and GPIOA */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

	/* Initialize Tx and Rx pins for USART1 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
	
	/* Initialize USART1 component */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART1, &USART_InitStructure);

  /* Enable the USARTx Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);	

	/* Enable USART1 component */
	USART_Cmd(USART1, ENABLE);
	
	/* Enable RXNE IRQ, while disable TXE IRQ for USART1 */
	USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	/* Reset all indexes of UART Tx/Rx buffers */
	__BUF_RESET(UART_TxBuffer.HeadIdx);
	__BUF_RESET(UART_TxBuffer.TailIdx);
}


/**
* @brief   		USART Rx Handler - forward byte to hook function
* @param  		None
* @retval 		None
*/
void UART_RxHandler() {
	/* Invoke hook function */
	UART_HookFunc_UART_IT_RXNE(USART_ReceiveData(USART1));
}


/**
* @brief   		USART Tx Handler - continue sending data if any
* @param  		None
* @retval 		None
*/
void UART_TxHandler() {
	if (!__BUF_IS_EMPTY(UART_TxBuffer.HeadIdx, UART_TxBuffer.TailIdx)) {
		/* Send earliest byte in buffer, then set Tx IRQ state */
		USART_SendData(USART1, UART_TxBuffer.Elements[UART_TxBuffer.TailIdx]);
		__BUF_INCR(UART_TxBuffer.TailIdx);
		UART_TxIrqState = SET;
	} else {
		/* Disable transmit interrupt, reset Tx IRQ state, invoke hook function */
		UART_TxIrqState = RESET;
		UART_HookFunc_UART_IT_TXE();
		USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
	}
}


/**
* @brief  UART IRQ Handler - verify and dispatch USART IRQ
* @param  None
* @retval None
*/
void USART1_IRQHandler() {
	/* Check UART RXNE interrupt */
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
		UART_RxHandler();
	}
	/* Check UART TXE interrupt */
	if (USART_GetITStatus(USART1, USART_IT_TXE) == SET) {
		UART_TxHandler();
	}
}


/**
* @brief			Check availability in UART Rx buffer
* @param			None
* @retval			Number of bytes in Rx buffer
*/
uint32_t UART_Ready() {
	return __BUF_REMAIN(UART_TxBuffer.HeadIdx, UART_TxBuffer.TailIdx);
}


/**
* @brief   		Write a byte to Tx buffer
* @param  		Byte to be appended into Tx ring buffer
* @retval 		None
*/
void UART_Write(uint8_t ch) {
	// Temporarily disable UART TXE interrupt to prevent index overwritten
	USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
	
	if (__BUF_IS_EMPTY(UART_TxBuffer.HeadIdx, UART_TxBuffer.TailIdx) && (UART_TxIrqState == RESET)) {
		/* Nothing in ring buffer, send immediately */
		USART_SendData(USART1, ch);
		UART_TxIrqState = SET;
	}
	else {
		if (!__BUF_IS_FULL(UART_TxBuffer.HeadIdx, UART_TxBuffer.TailIdx)) {
			/* Put new byte into ring buffer */
			UART_TxBuffer.Elements[UART_TxBuffer.HeadIdx] = ch;
			__BUF_INCR(UART_TxBuffer.HeadIdx);				
		}
		else {
			// Tx buffer overflow
			while(1);
		}
	}
	
	// Re-enables TXE Interrupt
	USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}

